/**
 * @file      : user_gpio.c
 * @brief     : 用户GPIO控制源文件
 * @author    : huenrong (huenrong1028@gmail.com)
 * @date      : 2021-03-23 21:01:22
 * @author    : huenrong
 *
 * @copyright : Copyright (c) 2021 胡恩荣
 *
 */

#include "./user_gpio.h"

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <poll.h>
#include <stdlib.h>

// KEY文件描述符
static int g_key_fd = -1;

/**
 * @brief  LED初始化
 * @return 成功: 0
 *         失败: -1
 */
int led_init(void)
{
    int ret = -1;

    // 导出GPIO
    ret = gpio_export(GPIO_LED);
    if (0 != ret)
    {
        return -1;
    }

    // 设置GPIO方向
    ret = gpio_set_direction(GPIO_LED, GPIO_OUT);
    if (0 != ret)
    {
        return -1;
    }

    // 设置GPIO默认值(默认关闭)
    ret = gpio_set_value(GPIO_LED, GPIO_HIGH);
    if (0 != ret)
    {
        return -1;
    }

    return 0;
}

/**
 * @brief  BEEP初始化
 * @return 成功: 0
 *         失败: -1
 */
int beep_init(void)
{
    int ret = -1;

    // 导出GPIO
    ret = gpio_export(GPIO_BEEP);
    if (0 != ret)
    {
        return -1;
    }

    // 设置GPIO方向
    ret = gpio_set_direction(GPIO_BEEP, GPIO_OUT);
    if (0 != ret)
    {
        return -1;
    }

    // 设置GPIO默认值(默认关闭)
    ret = gpio_set_value(GPIO_BEEP, GPIO_HIGH);
    if (0 != ret)
    {
        return -1;
    }

    return 0;
}

/**
 * @brief  KEY初始化
 * @return 成功: 0
 *         失败: -1
 */
int key_init(void)
{
    int ret = -1;

    // 导出GPIO
    ret = gpio_export(GPIO_KEY);
    if (0 != ret)
    {
        return -1;
    }

    // 设置GPIO方向
    ret = gpio_set_direction(GPIO_KEY, GPIO_IN);
    if (0 != ret)
    {
        return -1;
    }

    return 0;
}

/**
 * @brief  KEY初始化(使用poll方式读取键值)
 * @return 成功: 0
 *         失败: -1
 */
int key_init_with_poll(void)
{
    int ret = -1;

    // 导出GPIO
    ret = gpio_export(GPIO_KEY);
    if (0 != ret)
    {
        return -1;
    }

    // 设置GPIO方向
    ret = gpio_set_direction(GPIO_KEY, GPIO_IN);
    if (0 != ret)
    {
        return -1;
    }

    // 设置GPIO边沿
    ret = gpio_set_edge(GPIO_KEY, GPIO_BOTH);
    if (0 != ret)
    {
        return -1;
    }

    // 打开设备
    g_key_fd = gpio_open(GPIO_KEY);
    if (-1 == g_key_fd)
    {
        return -1;
    }

    return 0;
}

/**
 * @brief  LED 闪烁
 * @param  ms: 输入参数, 点亮时间(单位: ms)
 */
void led_blinking(const uint16_t ms)
{
    LED_ON();
    usleep(ms * 1000);
    LED_OFF();
}

/**
 * @brief  BEEP响
 * @param  ms: 输入参数, 时间(单位: ms)
 */
void beep_on(const uint16_t ms)
{
    BEEP_ON();
    usleep(ms * 1000);
    BEEP_OFF();
}

/**
 * @brief  BEEP循环响
 * @param  num: 输入参数, 蜂鸣器响的次数
 * @param  ms : 输入参数, 蜂鸣器单次响的时间(单位: ms)
 */
void beep_on_num(const uint16_t num, const uint16_t ms)
{
    for (uint16_t i = 0; i < num; i++)
    {
        beep_on(ms);
        usleep(ms * 1000);
    }
}

/**
 * @brief  读取按键键值
 * @param  value: 输出参数, 读取到的按键键值
 * @return 成功: 0
 *         失败: -1
 */
int read_key_value(enum gpio_value *value)
{
    int ret = -1;

    ret = gpio_get_value(value, GPIO_KEY);
    if (0 != ret)
    {
        return -1;
    }

    return 0;
}

/**
 * @brief  poll方式读取按键键值
 * @param  value: 输出参数, 读取到的按键键值
 * @return 成功: 0
 *         失败: -1
 *         超时: -2
 */
int read_key_value_with_poll(enum gpio_value *value)
{
    int ret = -1;
    // 指定fds数组中的项目数
    nfds_t nfds = 1;
    // 指定要监视的文件描述符集
    struct pollfd fds[1];
    // poll超时时间(单位: 毫秒)
    int timeout = 0;
    // 读取到的GPIO值
    char gpio_buf[2];

    // 设置超时时间为1s
    timeout = (1 * 1000);

    // 设置需要监听的文件描述符
    // 监听gpio时, events需要使用POLLPRI(表示有紧急数据需要读取), 不可使用POLLIN
    memset(fds, 0, sizeof(fds));
    fds[0].fd = g_key_fd;
    fds[0].events = POLLPRI;

    ret = poll(fds, nfds, timeout);
    // 返回负值, 发生错误
    if (ret < 0)
    {
        return -1;
    }
    // 返回0, 超时
    else if (0 == ret)
    {
        return -2;
    }
    // 返回值大于0, 成功
    else
    {
        // 判断是否是期望的返回
        if (fds[0].revents & POLLPRI)
        {
            memset(gpio_buf, 0, sizeof(gpio_buf));

            // 从文件起始位置开始读数据
            lseek(fds[0].fd, 0, SEEK_SET);
            ret = read(fds[0].fd, gpio_buf, 1);
            if (-1 != ret)
            {
                *value = atoi(gpio_buf);

                return 0;
            }
        }
    }

    return -1;
}
